package xyz.ddlnt.servicegeneral.wsServer;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import xyz.ddlnt.commonutil.constant.FieldConstants;
import xyz.ddlnt.commonutil.constant.MqConstants;
import xyz.ddlnt.model.vo.WsMessageVO;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author ZZULI_SE 210910
 * @data 2025/5/26 21:14
 */
@Slf4j
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
    // 静态变量解决多实例问题
    private static RabbitTemplate rabbitTemplate;

    // 必须保留无参构造器
    public WebSocketServer() {}

    // Spring注入方法
    @Autowired
    public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
        WebSocketServer.rabbitTemplate = rabbitTemplate;
    }



    /**
     * 静态变量，用来记录当前在线连接数，线程安全的类。
     */
    private static final AtomicInteger onlineSessionClientCount = new AtomicInteger(0);

    /**
     * 存放所有在线的客户端
     */
    private static final Map<String, Session> onlineSessionClientMap = new ConcurrentHashMap<>();
    /**
     * 存放用户当前聊天对象
     */
    private static final Map<String, String> onlineClientDialogTarget = new ConcurrentHashMap<>();
    private static final Map<String, String> onlineClientDialogTargetVersion = new ConcurrentHashMap<>();

    /**
     * 连接sid和连接会话
     */
    private String sid;

    /**
     * 连接建立成功调用的方法。由前端<code>new WebSocket</code>触发
     *
     * @param sid     每次页面建立连接时传入到服务端的id，比如用户id等。可以自定义。
     * @param session 与某个客户端的连接会话，需要通过它来给客户端发送消息
     */
    @OnOpen
    public void onOpen(@PathParam("sid") String sid, Session session) {
        onlineSessionClientMap.put(sid, session);
        //在线数加1
        onlineSessionClientCount.incrementAndGet();
        this.sid = sid;
    }

    /**
     * 连接关闭调用的方法。由前端<code>socket.close()</code>触发
     *
     * @param sid
     * @param session
     */
    @OnClose
    public void onClose(@PathParam("sid") String sid, Session session) throws IOException {
        // 从 Map中移除
        session.close();
        onlineSessionClientMap.remove(sid);
        onlineClientDialogTarget.remove(sid);
        onlineClientDialogTargetVersion.remove(sid);
        //在线数减1
        onlineSessionClientCount.decrementAndGet();
        log.info("连接session_id= {}，sid = {}关闭成功，当前在线数为：{}", onlineSessionClientCount, session.getId(), sid);
    }

    /**
     * 收到客户端消息后调用的方法。由前端<code>socket.send</code>触发
     * 可以接收 JSON 数据: 通过将消息参数类型设置为 String
     * 当服务端执行toSession.getAsyncRemote().sendText(xxx)后，前端的socket.onmessage得到监听。
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {

        // 取值：{"toSid":"user-1","message":"hello websocket"} （严格的JSON字符串，不能写JSON对象）
        JSONObject jsonObject = JSON.parseObject(message);
        String receiverId = jsonObject.getString(FieldConstants.RECEIVER_ID);
        String senderVersion = jsonObject.getString(FieldConstants.SENDER_VERSION);
        log.info(receiverId.toString());
        log.info(senderVersion.toString());

        String content = jsonObject.getString(FieldConstants.CONTENT);
        String isImg = jsonObject.getString(FieldConstants.IS_IMG);
        String receiverVersion = jsonObject.getString(FieldConstants.RECEIVER_VERSION);


        // 设置用户当前选中的聊天对象
        String targetId = onlineClientDialogTarget.get(this.sid);
        if (targetId == null || "".equalsIgnoreCase(targetId) || !targetId.equals(receiverId)) {
            onlineClientDialogTarget.put(this.sid, receiverId);
            onlineClientDialogTargetVersion.put(this.sid, senderVersion);
        }


        String tempReceiverVersion = onlineClientDialogTargetVersion.get(receiverId);
        if (tempReceiverVersion != null && !tempReceiverVersion.isEmpty() && !tempReceiverVersion.equals(senderVersion)) {
            receiverVersion = tempReceiverVersion;
        }
        // 如果消息非空，则向toSid发送消息
        if (content != null && !"".equalsIgnoreCase(content)) {
            sendToOne(receiverId, isImg, content, senderVersion, receiverVersion);
        }
    }

    /**
     * 发送消息
     * @param receiverId
     * @param content
     */
    private void sendToOne(String receiverId, String isImg, String content, String senderVersion, String receiverVersion) {
        Map<String, String> map = new HashMap<>();
        map.put(FieldConstants.SENDER_ID, this.sid);
        map.put(FieldConstants.RECEIVER_ID, receiverId);
        map.put(FieldConstants.IS_IMG, isImg);
        map.put(FieldConstants.CONTENT, content);
        map.put(FieldConstants.SENDER_VERSION, senderVersion);
        map.put(FieldConstants.RECEIVER_VERSION, receiverVersion);
        LocalDateTime now = LocalDateTime.now();
        map.put(FieldConstants.SEND_TIME, now.toString());

        // 判断toSid是否在线
        Session toSession = onlineSessionClientMap.get(receiverId);
        if (toSession == null) {  // toSid不在线
            log.error("用户 {} 向用户 {} 发送消息 ==> 用户 {} 不在线！",this.sid, receiverId, receiverId);
            // toSid未读消息+1
            rabbitTemplate.convertAndSend(MqConstants.USR_PRIVATE_MESSAGE_LIST, MqConstants.UNREAD_MESSAGES_PLUS_ONE, map);
            // 提交队列，持久化未读聊天信息
            rabbitTemplate.convertAndSend(MqConstants.USR_MESSAGE, MqConstants.MESSAGE_PLUS_ONE, map);
            return;
        }

        // toSid在线，判断toSid私信是否为用户
        String sid = onlineClientDialogTarget.get(receiverId);
        if (sid == null || "".equalsIgnoreCase(sid) ||  !sid.equalsIgnoreCase(this.sid)) {  //  toSid私信对象不是用户
            log.error("用户 {} 向用户 {} 发送消息 ==> 用户 {} 正在在与他人私信！",this.sid, receiverId, receiverId);
            // toSid未读消息+1
            rabbitTemplate.convertAndSend(MqConstants.USR_PRIVATE_MESSAGE_LIST, MqConstants.UNREAD_MESSAGES_PLUS_ONE, map);
            // 提交队列，持久化未读聊天信息
            rabbitTemplate.convertAndSend(MqConstants.USR_MESSAGE, MqConstants.MESSAGE_PLUS_ONE, map);
            return;
        }

        // 异步发送
        log.info("用户 {} 向用户 {} 发送了消息 ==> 发送的消息为： message = {}",this.sid, receiverId, content);
        WsMessageVO msg = WsMessageVO.builder()
                .isImg(Boolean.parseBoolean(isImg))
                .content(content)
                .sendTime(now)
                .build();
        toSession.getAsyncRemote().sendText(JSONObject.toJSONString(msg));
        // 提交队列，持久化已读聊天信息
        rabbitTemplate.convertAndSend(MqConstants.USR_MESSAGE, MqConstants.MESSAGE_PLUS_ONE, map);
    }

    /**
     * 发生错误调用的方法
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误，错误信息为：{}", error.getMessage());
        error.printStackTrace();
    }
}
